package newspapers.autopublishing;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;

public class ClientControl {

    public static int client_num = 2; //TODO: this shouldn't be hard coded (Bea)

    private double time_step = 0.5;   // time interval in seconds
    //TODO: this shouldn't be hard coded (Bea)

    private double sleep_time = 10;   // time interval in milli-seconds
    //TODO: this shouldn't be hard coded (Bea)

    //TODO: this shouldn't be hard coded (Bea)
    public ArrayList<NewspaperConfig> publishers;

    private int timer;

    public AutoNewspaper[] newspapers;

    private int[] publish_time;

    //time the GEMOM clients should update their setting by loading
    //new time segment descriptor
    //negative value suggests publish tasks in each time segment has
    //finished/not started
    private int[] update_seg_time;

    private boolean[] finished;

    private boolean stop;

    protected DateFormat m_sdf = SimpleDateFormat
            .getTimeInstance(SimpleDateFormat.MEDIUM);

    private Thread thread;

//    ORIGINAL
//    public static void main(String[] args) {
//        new ClientControl();
//    }

    public ClientControl(ArrayList<NewspaperConfig> publishers,
                         AutoNewspaper[] newspapers) {

        this.publishers = publishers;
        this.newspapers = newspapers;

        stop = false;

//        ORIGINAL
//        //TODO: this shouldn't be hard coded and there could be different
//        //topics for each newspaper
//        ArrayList<String> topics = new ArrayList<String>();
//        topics.add(new String("sport"));
//        topics.add(new String("music"));
//        topics.add(new String("arts"));
//
//        //TODO: this shouldn't be hard coded
//        NewspaperConfig pubConfig1 = new NewspaperConfig();
//        pubConfig1.username = "guest";
//        pubConfig1.password = "guest";
//        pubConfig1.name = "The Guardian";
//        pubConfig1.topic_prefix = "guardian";
//        pubConfig1.topics = topics;
//        TimeSeg ts11 = new TimeSeg();
//        ts11.t_id = 11;
//        ts11.time_start = 1;
//        ts11.time_stop = 1000;
//        ts11.distribution = "exp";
//        ts11.message_length_distribution = "uni";
//        ts11.message_length = 10;
//        ts11.mld_std_deviation = 2;
//        ts11.ingressNode = "root";
//        ts11.mean_rate = 2;
//        ts11.min_rate = 1;
//        ts11.max_rate = 3;
//        ts11.use_mean_rate = false;
//        ts11.topicName = "sport";
//        TimeSeg ts12 = new TimeSeg();
//        ts12.t_id = 12;
//        ts12.time_start = 1001;
//        ts12.time_stop = 2000;
//        ts12.distribution = "exp";
//        ts12.message_length_distribution = "uni";
//        ts12.message_length = 50;
//        ts12.mld_std_deviation = 1;
//        ts12.ingressNode = "root";
//        ts12.mean_rate = 1.75;
//        ts12.min_rate = 0.5;
//        ts12.max_rate = 4;
//        ts12.use_mean_rate = false;
//        ts12.topicName = "music";
//        pubConfig1.tsegs.add(ts11);
//        pubConfig1.tsegs.add(ts12);
//        publishers.add(pubConfig1);
//
//        //TODO: this shouldn't be hard coded
//        NewspaperConfig pubConfig2 = new NewspaperConfig();
//        pubConfig2.username = "guest";
//        pubConfig2.password = "guest";
//        pubConfig2.name = "The Telegraph";
//        pubConfig2.topic_prefix = "telegraph";
//        pubConfig2.topics = topics;
//        TimeSeg ts21 = new TimeSeg();
//        ts21.t_id = 11;
//        ts21.time_start = 1;
//        ts21.time_stop = 1000;
//        ts21.distribution = "exp";
//        ts21.message_length_distribution = "uni";
//        ts21.message_length = 20;
//        ts21.mld_std_deviation = 0.5;
//        ts21.ingressNode = "root";
//        ts21.mean_rate = 2;
//        ts21.min_rate = 1;
//        ts21.max_rate = 3;
//        ts21.use_mean_rate = false;
//        ts21.topicName = "sport";
//        TimeSeg ts22 = new TimeSeg();
//        ts22.t_id = 12;
//        ts22.time_start = 1001;
//        ts22.time_stop = 2000;
//        ts22.distribution = "exp";
//        ts22.message_length_distribution = "uni";
//        ts22.message_length = 65;
//        ts22.mld_std_deviation = 3;
//        ts22.ingressNode = "root";
//        ts22.mean_rate = 1.75;
//        ts22.min_rate = 0.5;
//        ts22.max_rate = 4;
//        ts22.use_mean_rate = false;
//        ts22.topicName = "arts";
//        pubConfig2.tsegs.add(ts21);
//        pubConfig2.tsegs.add(ts22);
//        publishers.add(pubConfig2);

        initPublishers();

        startPublishers();

    }

    public class ApThread extends Thread {
        public void run() {
            doPublish();
            return;
        }
    }

    // loop to publishing
    private void doPublish() {

        System.out.println(getTimeAsString() + ": Scenario started");

        //printStat();

        timer = 0;

        while (true) {
            for (int j = 0; j < client_num; j++) {
                if (publish_time[j] == timer && !finished[j]) {
                    if (newspapers[j].active) {
                        //Publish a message with a random text
                        newspapers[j].onPublish(timer);
                        updatePublishTime(timer, j);
                    }
                }

                if (update_seg_time[j] == timer && !finished[j]) {
                    updateTasks(timer, j);
                }
            }


            if (check()) {
                stop = true;
                for (int i = 0; i < client_num; i++) {
                    //reset newspapers
                    newspapers[i].updateNewspaperQpid(
                            publishers.get(i).tsegs.get(0));
                    newspapers[i].tSegIndex = 0;
//                    ORIGINAL
//                    newspapers[i].closeConnection();
                }
                System.out.println(getTimeAsString() + ": Scenario finished");
            }

            waiting(sleep_time);
            timer++;
            if (stop) {
                break;
            }
        }

    }

    public int getTimeSlot() {
        return timer;
    }

    // wait a constant time
    private void waiting(double time) {

        try {
            Thread.sleep((int) time);
        } catch (Exception e) {
        }

    }

    private void initTask() {

        for (int i = 0; i < client_num; i++) {
            finished[i] = false;
            int time = (int) (publishers.get(i).tsegs.get(0).timeStart
                    / time_step);

            // set the time that each publisher's story setting should change

            update_seg_time[i] = (int) (publishers.get(i).tsegs.get(0).timeStop
                    / time_step);

            updateTasks(time, i);
        }
    }

    private boolean check() {
        boolean done = true;
        for (int i = 0; i < client_num; i++) {
            if (!finished[i]) {
               done = false;
            }
        }
        return done;
    }

    private void initPublishers() {

        publish_time = new int[client_num];

        update_seg_time = new int[client_num];

        finished = new boolean[client_num];

//        ORIGINAL
//        newspapers= new AutoNewspaper[client_num];
//
//        for(int i=0; i<client_num; i++) {
//            newspapers[i]= new AutoNewspaper(publishers.get(i).name,
//                    publishers.get(i).username, publishers.get(i).password,
//                    publishers.get(i).topic_prefix, publishers.get(i).topics,
//                    i, publishers.get(i).tsegs.get(0), this);
//        }

        System.out.println(getTimeAsString() + ": Scenario ready to go");

    }

    private void startPublishers() {

        initTask();

        thread = new ApThread();

        stop = false;
        // Start the thread
        thread.start();

    }

    // check whether there are more time segments description for the
    //specific publisher, load
    // the new segment whenever it applies and set the new update time tag,
    //publish time tag, finished
    // flag and set them as finished when there are no more time tasks for
    //the publisher
    public void updateTasks(int time_slot, int publisher_index) {

        if (update_seg_time[publisher_index] == time_slot) {
            int seg_index = newspapers[publisher_index].tSegIndex;
            if (publishers.get(publisher_index).tsegs.size() <= seg_index + 1) {
                update_seg_time[publisher_index] = -1;
                finished[publisher_index] = true;
                publish_time[publisher_index] = -1;
            } else {
                //There are certainly more time segment,
                //load new time segment settings

                TimeSeg seg = publishers.get(publisher_index).tsegs.get(
                        seg_index + 1);
                newspapers[publisher_index].updateNewspaperQpid(seg);
                update_seg_time[publisher_index]
                        = (int) (seg.timeStop / time_step);
                updatePublishTime((int) (seg.timeStart / time_step),
                        publisher_index);
            }
        } else {
            updatePublishTime(time_slot, publisher_index);
        }
    }

    private void updatePublishTime(int time_slot, int publisher_index) {

        if (!finished[publisher_index]) {

            Random r = new Random();

             double rl = r.nextDouble();
            while (rl == 0) {
                rl = r.nextDouble();
            }
            double y = 0;
            //get the next publish time which has the time interval follows
            //an exponential distribution
            if (newspapers[publisher_index].getDistribution() == 0) {
                 y = AutoGen.getNextPublishTimeExp(
                         newspapers[publisher_index].messageRate, rl);
            }
            if (newspapers[publisher_index].getDistribution() == 1) {
                 y = AutoGen.getNextPublishTimeUni(
                         newspapers[publisher_index].messageRate);
            }

            //the relative next publish time has to be transformed into a
            //time slot indicator
            int nexttime = (int) (y / time_step + 1);

            publish_time[publisher_index] = time_slot + nexttime;
        }

    }

    protected String getTimeAsString() {
        return m_sdf.format(new Date());
    }

}
